/*
 *   Copyright 2021 zzh
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *
 */

package com.aduib.boot.common.json;

import com.aduib.boot.common.util.DateTimePattern;
import com.fasterxml.jackson.core.JsonGenerator.Feature;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.json.JsonReadFeature;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;

import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.TimeZone;

/**
 * @description: JacksonParser
 * @author: zzh
 * @date: 2021/9/3 13:33
 */
public class JacksonParser implements JsonParser {

  @SuppressWarnings("deprecation")
  public static final ObjectMapper objectMapper;

  public static final LocalDateSerializer localDateSerializer =
      new LocalDateSerializer(DateTimeFormatter.ofPattern(DateTimePattern.DATE_FORMAT));

  public static final LocalTimeSerializer localTimeSerializer =
      new LocalTimeSerializer(DateTimeFormatter.ofPattern(DateTimePattern.TIME_FORMAT));

  public static final LocalDateTimeSerializer localDateTimeSerializer =
      new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DateTimePattern.DATE_TIME_FORMAT));

  public static final LocalDateDeserializer localDateDeserializer =
      new LocalDateDeserializer(DateTimeFormatter.ofPattern(DateTimePattern.DATE_FORMAT));

  public static final LocalTimeDeserializer localTimeDeserializer =
      new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DateTimePattern.TIME_FORMAT));

  public static final LocalDateTimeDeserializer localDateTimeDeserializer =
      new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DateTimePattern.DATE_TIME_FORMAT));

  static {
    Jdk8Module jdk8Module = new Jdk8Module();
    JavaTimeModule javaTimeModule = new JavaTimeModule();
    javaTimeModule.addSerializer(LocalDate.class, localDateSerializer);
    javaTimeModule.addSerializer(LocalTime.class, localTimeSerializer);
    javaTimeModule.addSerializer(LocalDateTime.class, localDateTimeSerializer);
    javaTimeModule.addDeserializer(LocalDate.class, localDateDeserializer);
    javaTimeModule.addDeserializer(LocalTime.class, localTimeDeserializer);
    javaTimeModule.addDeserializer(LocalDateTime.class, localDateTimeDeserializer);
    //        javaTimeModule.addSerializer(new )
    SimpleModule simpleModule = new SimpleModule();
    simpleModule.addSerializer(Long.class, new ToStringSerializer());
    objectMapper =
        new ObjectMapper()
            .registerModules(jdk8Module, javaTimeModule, new ParameterNamesModule(), simpleModule)
            .enable(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature())
            .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) // 禁止使用出现未知属性之时，抛出异常
            .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
            .disable(Feature.WRITE_BIGDECIMAL_AS_PLAIN)
            .setLocale(Locale.CANADA)
            .setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"))
            //            .setSerializationInclusion(JsonInclude.Include.NON_NULL)
            //            .setSerializationInclusion(JsonInclude.Include.NON_DEFAULT)
            .setDateFormat(new SimpleDateFormat(DateTimePattern.DATE_TIME_FORMAT));
    objectMapper.setSerializerFactory(
        objectMapper.getSerializerFactory().withSerializerModifier(new NullSerializerModifier()));
  }
  //
  // .setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);//
  // 转化后的json的key命名格式

  /**
   * 将对象转换成json字符串
   *
   * @param <T>
   * @return
   */
  public static <T> String writeStringByObject(T t) {
    try {
      return objectMapper.writeValueAsString(t);
    } catch (JsonProcessingException e) {
      e.printStackTrace();
    }
    return "";
  }

  /**
   * 从字符串中读取对象
   *
   * @param str
   * @param clazz
   * @return
   */
  public static <T> Object readObjectByString(String str, Class<T> clazz) {
    try {
      return objectMapper.readValue(str, clazz);
    } catch (IOException e) {
      e.printStackTrace();
    }
    return clazz;
  }

  /**
   * 从输入流读取对象
   *
   * @param src
   * @param clazz
   * @param <T>
   * @return
   */
  public static <T> Object readObjectByInputStream(InputStream src, Class<T> clazz) {
    try {
      return ((T) objectMapper.readValue(src, clazz));
    } catch (IOException e) {
      e.printStackTrace();
    }
    return clazz;
  }

  /**
   * @param str
   * @param collectionType
   * @param <E>
   * @return
   */
  public static <E> E readObjectByString(String str, JavaType collectionType) {
    try {
      return objectMapper.readValue(str, collectionType);
    } catch (IOException e) {
      e.printStackTrace();
    }
    return null;
  }

  /**
   * @param src
   * @param collectionType
   * @param <E>
   * @return
   */
  public static <E> E readObjectByInputStream(InputStream src, JavaType collectionType) {
    try {
      return objectMapper.readValue(src, collectionType);
    } catch (IOException e) {
      e.printStackTrace();
    }
    return null;
  }

  public static JsonNode readJsonTreeByInputStream(InputStream inputStream) {
    try {
      return objectMapper.readTree(inputStream);
    } catch (IOException e) {
      e.printStackTrace();
    }
    return null;
  }

  public static JsonNode readJsonTreeByString(String str) {
    try {
      return objectMapper.readTree(str);
    } catch (IOException e) {
      e.printStackTrace();
    }
    return null;
  }

  /**
   * 获取泛型的Collection Type
   *
   * @param collectionClass 泛型的Collection
   * @param elementClasses 元素类
   * @return JavaType Java类型
   * @since 1.0
   */
  public static JavaType getCollectionType(Class<?> collectionClass, Class<?>... elementClasses) {
    return objectMapper.getTypeFactory().constructParametricType(collectionClass, elementClasses);
  }

  public static JavaType getJavaType(Class<?> elementClasses) {
    return objectMapper.getTypeFactory().constructType(elementClasses);
  }

  /**
   * 将map转化为json字符串
   *
   * @param jsonmap
   * @return
   */
  public static String writeJsonStringByMap(Map<String, Object> jsonmap) {
    return writeStringByObject(jsonmap);
  }
  /**
   * 查找值
   *
   * @param jsonstring
   * @param key
   * @param value
   * @return
   */
  public static List<String> findValuesbyJsonString(String jsonstring, String key, Object value) {
    List<String> jsonList = new ArrayList<>();
    JsonNode node = null;
    try {
      node = objectMapper.readTree(jsonstring);
      jsonList = jsonNodeParser(node, key, value);
    } catch (IOException e) {
      e.printStackTrace();
    }
    return jsonList;
  }

  /**
   * Jackson library
   *
   * @param jsonInString
   * @return
   */
  public static boolean isJSONValid(String jsonInString) {
    try {
      final ObjectMapper mapper = new ObjectMapper();
      mapper.readTree(jsonInString);
      return true;
    } catch (IOException e) {
      return false;
    }
  }

  /**
   * json字符串解析 获取的特定值的集合
   *
   * @param node
   * @param key
   * @param value
   * @return
   */
  public static List<String> jsonNodeParser(JsonNode node, String key, Object value) {
    List<String> jsonList = new ArrayList<>();
    if (node.isArray()) {
      Iterator<JsonNode> it = node.iterator();
      while (it.hasNext()) {
        JsonNode jsonNode = it.next();
        if (jsonNode.isValueNode()) {
          jsonList.add(node.toString());
        }
        if (jsonNode.isObject()) {
          Iterator<Map.Entry<String, JsonNode>> it1 = jsonNode.fields();
          while (it1.hasNext()) {
            Map.Entry<String, JsonNode> entry = it1.next();
            if (entry.getKey().equals(key) && entry.getValue().asText().equals(value)) {
              jsonList.add(jsonNode.toString());
            }
          }
        }
      }
    }
    return jsonList;
  }

  @Override
  public boolean validate(String jsonString) {
    return isJSONValid(jsonString);
  }

  @Override
  public <T> T read(String jsonString, TypeCompositeToJson type) {
    Class<?> elementClass = (Class<?>) type.getElementType();
    Class<?> containerClass = (Class<?>) type.getContainerType();
    T t = null;
    try {
      if (Objects.nonNull(type.getContainerType())) {
        t = readObjectByString(jsonString, getCollectionType(containerClass, elementClass));
      } else if (Objects.isNull(containerClass) && elementClass.isArray()) {
        t = (T) readObjectByString(jsonString, elementClass);
      } else {
        t = objectMapper.readValue(jsonString, getJavaType(elementClass));
      }
    } catch (JsonProcessingException e) {
      e.printStackTrace();
      return null;
    }
    return t;
  }

  @Override
  public <T> String write(T object) {
    return writeStringByObject(object);
  }
}
